package com.ale.oxetool.ws;


import com.ale.oxetool.exceptions.BusinessException;
import com.ale.oxetool.service.PhoneService;
import com.ale.oxetool.ws.phone_switch_service.CcGetData;
import com.ale.oxetool.ws.phone_switch_service.CcGetDataResponse;
import generated.Data;
import generated.ORDER;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.springframework.xml.transform.StringSource;

import javax.annotation.PostConstruct;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by guoshunw on 2016/3/29.
 * https://stackoverflow.com/questions/3846121/changing-the-default-xml-namespace-prefix-generated-with-jaxws
 * https://stackoverflow.com/questions/16616544/removing-namespace-prefix-in-the-output-of-spring-ws-web-service
 * https://stackoverflow.com/questions/34353108/soap-without-namespace-in-response-spring-boot
 */

@Endpoint
public class PhoneSwitchEndPoint {

    public static final String NAMESPACE_URI = "http://oxetool.ale.com/ws/phone-switch-service";

    private static Logger log = LoggerFactory.getLogger(PhoneSwitchEndPoint.class);

    private static final int INTERNAL_ERROR = 0;
    private static final int MESSAGE_PARSING_ERROR = 1;
    private static final int SQL_EXECUTION_ERROR = 2;
    private static final int SQL_EXECUTION_TIMEOUT = 3;
    private static final int QUERYING_RESULT_EMPTY = 4;
    private static final int PARAMETER_EMPTY = 5;
    private static final int INVALID_USER_NAME_AND_PASSWORD = 6;
    private static final int USER_CONNECTION_FAILURE = 7;
    private static final int PERMISSION_DENIED = 8;
    private static final int INVALID_CLIENT_IP_ADDRESS = 9;

    private static final int TEST_ERR1 = 100;
    private static final int TEST_ERR2  = 99;


    private static final int NO_ERROR = 65535;


    private Unmarshaller jaxbOrderUnmarshaller;
    private Marshaller jaxbDataMarshaller;

    @Value("${ws.site.servicecode}")
    private String siteServiceCode;

    private Map<Integer, String> errors= new HashMap<>();

    @Autowired
    private PhoneService phoneService;

    private List<String> validTargetPlatforms = Arrays.asList("01", "02", "1", "2");


    @PostConstruct
    private void init() {

        errors.put(INTERNAL_ERROR, "内部错误");
        errors.put(MESSAGE_PARSING_ERROR, "报文解析错误");
        errors.put(SQL_EXECUTION_ERROR, "SQL执行错误");
        errors.put(SQL_EXECUTION_TIMEOUT, "SQL执行超时");
        errors.put(QUERYING_RESULT_EMPTY, "查询结果数据为空");
        errors.put(PARAMETER_EMPTY, "传入参数数据为空");
        errors.put(INVALID_USER_NAME_AND_PASSWORD, "用户名和密码不匹配");
        errors.put(USER_CONNECTION_FAILURE, "服务连接失败");
        errors.put(PERMISSION_DENIED, "没有服务访问权限");
        errors.put(INVALID_CLIENT_IP_ADDRESS, "非法的客户端IP访问");

        errors.put(TEST_ERR1, "测试错误1");
        errors.put(TEST_ERR2, "测试错误2");

        try {
            JAXBContext jaxbOrderContext = JAXBContext.newInstance(ORDER.class);
            jaxbOrderUnmarshaller = jaxbOrderContext.createUnmarshaller();


            JAXBContext jaxbDataContext = JAXBContext.newInstance(Data.class);
            jaxbDataMarshaller = jaxbDataContext.createMarshaller();
            jaxbDataMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            jaxbDataMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);

        } catch (JAXBException e) {
            log.error("Initialize jaxb context instance fall.");
            e.printStackTrace();
        }
    }

    private CcGetDataResponse getResponseData(int errorCode) {

        CcGetDataResponse response = new CcGetDataResponse();
        if (NO_ERROR == errorCode) {
            response.setOut("<data></data>");
            return response;
        }

        Data data = new Data();

        data.setErrorCode(errorCode);
        data.setErrorMessage(errors.get(errorCode));


        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            jaxbDataMarshaller.marshal(data, os);
        } catch (JAXBException e) {
            log.error("Marshalling data for error code {} error", errorCode);
            e.printStackTrace();
        }
        response.setOut(new String(os.toByteArray(), StandardCharsets.UTF_8));
        return response;
    }

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "ccGetData")
    @ResponsePayload
//                       The state grid need the operation name must be ccGetData
    public CcGetDataResponse ccGetData(@RequestPayload CcGetData request) {
        String input = request.getIn0();
        log.info("Soap input parameter = {}\n", input);
        try {
            ORDER order = (ORDER) jaxbOrderUnmarshaller.unmarshal(new StringSource(input));
            if (!siteServiceCode.equals(order.getServicecode())) {
                return getResponseData(PERMISSION_DENIED);
            }

            if (!validTargetPlatforms.contains(order.getData().getPlatform())) {
                log.info("Invalid target platform {}, should be 01/02.", order.getData().getPlatform());
                return getResponseData(MESSAGE_PARSING_ERROR);
            }
            log.info("Platform={}, number={}", order.getData().getPlatform(), order.getData().getNumber());
            phoneService.switchPhone(order.getData().getPlatform(), order.getData().getNumber());
        } catch (JAXBException e) {
            log.info(e.toString());
            return getResponseData(MESSAGE_PARSING_ERROR);
        } catch (BusinessException | IOException e ) {
            log.info(e.toString());
            return getResponseData(INTERNAL_ERROR);
        }catch (Exception e){
            e.printStackTrace();
            return getResponseData(INTERNAL_ERROR);
        }

        return getResponseData(NO_ERROR);
    }

}
